package com.gccloud.design.core.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.gccloud.design.core.dao.BaseDao;
import com.gccloud.design.utils.WrapperUtils;
import com.gccloud.idc.security.shiro.UserUtils;
import com.gccloud.starter.common.constant.GlobalConst;
import com.gccloud.starter.common.dto.SearchDTO;
import com.gccloud.starter.common.exception.GlobalException;
import com.gccloud.starter.mybatis.page.PageVO;
import com.gccloud.starter.mybatis.utils.QueryWrapperUtils;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.reflection.property.PropertyNamer;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public interface ISuperService<T> extends IService<T> {

    /**
     * 属性和列名映射
     */
    Map<String, String> PROPERTY_COLUMN_MAP = Maps.newHashMap();

    /**
     *
     * 获取BaseDao
     *
     * @return
     */
    default BaseDao<T> getBaseDao() {
        return (BaseDao<T>) getBaseMapper();
    }

    /**
     * 根据 ID 选择修改
     *
     * @param entity 实体对象
     */
    default boolean updateByIdWithDp(T entity) {
        return SqlHelper.retBool(getBaseDao().updateByIdWithDp(entity));
    }

    /**
     * 根据 UpdateWrapper 条件，更新记录 需要设置sqlset
     *
     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
     */
    default boolean updateWithDp(Wrapper<T> updateWrapper) {
        return updateWithDp(null, updateWrapper);
    }

    /**
     * 根据 whereEntity 条件，更新记录
     *
     * @param entity        实体对象
     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
     */
    default boolean updateWithDp(T entity, Wrapper<T> updateWrapper) {
        return SqlHelper.retBool(getBaseDao().updateWithDp(entity, updateWrapper));
    }

    /**
     * 根据ID删除
     *
     * @param id
     * @return
     */
    default int deleteById(Serializable id) {
        return getBaseMapper().deleteById(id);
    }

    /**
     * 根据条件删除
     *
     * @param wrapper
     * @return
     */
    default int delete(Wrapper<T> wrapper) {
        return getBaseMapper().delete(wrapper);
    }

    /**
     * 根据ID 删除（带有数据权限）
     *
     * @param id
     * @return
     */
    default int deleteByIdWithDp(Serializable id) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(GlobalConst.ColumnField.ID, id);
        WrapperUtils.wrapDp(queryWrapper);
        return getBaseMapper().delete(queryWrapper);
    }

    /**
     * 根据条件删除（带有数据权限）
     *
     * @param wrapper
     * @return
     */
    default int deleteWithDp(QueryWrapper<T> wrapper) {
        WrapperUtils.wrapDp(wrapper);
        return getBaseMapper().delete(wrapper);
    }

    /**
     * 根据条件删除（带有数据权限）
     *
     * @param wrapper
     * @return
     */
    default int deleteWithDp(LambdaQueryWrapper<T> wrapper) {
        WrapperUtils.wrapDp(wrapper);
        return getBaseMapper().delete(wrapper);
    }

    /**
     * 根据主键查询（带有数据权限）
     *
     * @param id
     * @return
     */
    default T getByIdWithDp(Serializable id) {
        QueryWrapper<T> query = Wrappers.query();
        query.eq(GlobalConst.ColumnField.ID, id);
        WrapperUtils.wrapDp(query);
        List<T> list = list(query);
        if (list == null || list.size() == 0) {
            return null;
        }
        return list.get(0);
    }

    /**
     * 查询（带有数据权限）
     *
     * @return
     */
    default List<T> listWithDp() {
        QueryWrapper<T> query = Wrappers.query();
        List<T> list = listWithDp(query);
        return list;
    }


    /**
     * 查询（带有数据权限）
     *
     * @param queryWrapper
     * @return
     */
    default List<T> listWithDp(QueryWrapper<T> queryWrapper) {
        if (queryWrapper == null) {
            queryWrapper = Wrappers.query();
        }
        WrapperUtils.wrapDp(queryWrapper);
        return list(queryWrapper);
    }

    /**
     * 查询（带有数据权限）
     *
     * @param queryWrapper
     * @return
     */
    default List<T> listWithDp(LambdaQueryWrapper<T> queryWrapper) {
        if (queryWrapper == null) {
            queryWrapper = Wrappers.lambdaQuery();
        }
        WrapperUtils.wrapDp(queryWrapper);
        return list(queryWrapper);
    }


    ////////////////////////////////////////////////////////////////////////////////
    //   分页查询
    ////////////////////////////////////////////////////////////////////////////////

    /**
     * 翻页查询（带有数据权限）
     *
     * @param page         翻页对象
     * @param queryWrapper 实体对象封装操作类 {@link QueryWrapper}
     */
    default <E extends IPage<T>> E pageWithDp(E page, QueryWrapper<T> queryWrapper) {
        WrapperUtils.wrapDp(queryWrapper);
        return getBaseDao().selectPage(page, queryWrapper);
    }

    /**
     * 翻页查询（带有数据权限）
     *
     * @param page         翻页对象
     * @param queryWrapper 实体对象封装操作类 {@link LambdaQueryWrapper}
     */
    default <E extends IPage<T>> E pageWithDp(E page, LambdaQueryWrapper<T> queryWrapper) {
        WrapperUtils.wrapDp(queryWrapper);
        return getBaseDao().selectPage(page, queryWrapper);
    }

    /**
     * 翻页查询（带有数据权限）
     *
     * @param page 翻页对象
     * @see Wrappers#emptyWrapper()
     */
    default <E extends IPage<T>> E pageWithDp(E page) {
        QueryWrapper<T> query = Wrappers.query();
        WrapperUtils.wrapDp(query);
        return page(page, query);
    }

    /**
     * 分页查询
     *
     * @param searchDTO
     * @param queryWrapper
     * @return
     */
    default PageVO<T> page(SearchDTO searchDTO, LambdaQueryWrapper<T> queryWrapper) {
        Page<T> searchPage = packSearchPage(searchDTO);
        Page<T> page = this.page(searchPage, queryWrapper);
        return new PageVO<T>(page);
    }

    /**
     * 分页查询
     *
     * @param searchDTO
     * @param queryWrapper
     * @return
     */
    default PageVO<T> pageWithDp(SearchDTO searchDTO, LambdaQueryWrapper<T> queryWrapper) {
        Page<T> searchPage = packSearchPage(searchDTO);
        WrapperUtils.wrapDp(queryWrapper);
        Page<T> page = this.page(searchPage, queryWrapper);
        return new PageVO<T>(page);
    }

    /**
     * 分页查询
     *
     * @param searchDTO
     * @param fieldNames
     * @return
     */
    default PageVO<T> page(SearchDTO searchDTO, SFunction<T, ?>... fieldNames) {
        LambdaQueryWrapper<T> queryWrapper = packQueryWrapper(searchDTO, fieldNames);
        return page(searchDTO, queryWrapper);
    }

    /**
     * 分页查询（带有数据权限）
     *
     * @param searchDTO
     * @param fieldNames
     * @return
     */
    default PageVO<T> pageWithDp(SearchDTO searchDTO, SFunction<T, ?>... fieldNames) {
        LambdaQueryWrapper<T> queryWrapper = packQueryWrapper(searchDTO, fieldNames);
        return pageWithDp(searchDTO, queryWrapper);
    }

    /**
     * 封装查询器
     *
     * @param searchDTO
     * @param fieldNames
     * @return
     */
    default LambdaQueryWrapper<T> packQueryWrapper(SearchDTO searchDTO, SFunction<T, ?>... fieldNames) {
        LambdaQueryWrapper<T> queryWrapper = QueryWrapperUtils.wrapperLike(new LambdaQueryWrapper<T>(), searchDTO.getSearchKey(), fieldNames);
        return queryWrapper;
    }

    /**
     * 封装分页查询的分页条件
     *
     * @param searchDTO
     * @return
     */
    default Page<T> packSearchPage(SearchDTO searchDTO) {
        Page<T> searchPage = new Page<>();
        if (searchDTO.getCurrent() == null || searchDTO.getCurrent() <= 0) {
            searchPage.setCurrent(1);
        } else {
            searchPage.setCurrent(searchDTO.getCurrent());
        }
        Integer current = searchDTO.getCurrent();
        if (current == null) {
            current = 1;
        }
        Integer size = searchDTO.getSize();
        if (size == null || size <= 0) {
            size = 10;
        }
        if (size > 500) {
            size = 500;
        }
        searchPage.setSize(size);
        searchPage.setCurrent(current);
        return searchPage;
    }
    ////////////////////////////////////////////////////////////////////////////////
    //   判重逻辑
    ////////////////////////////////////////////////////////////////////////////////

    /**
     * 全局唯一
     *
     * @param id
     * @param idValue
     * @param field
     * @param value
     * @return
     */
    default boolean repeatGlobal(SFunction<T, ?> id, Serializable idValue, SFunction<T, ?> field, Object value) {
        return repeat(id, idValue, field, value, GlobalConst.RepeatStrategy.GLOBAL);
    }

    /**
     * 应用下唯一
     *
     * @param id
     * @param idValue
     * @param field
     * @param value
     * @return
     */
    default boolean repeatGlobal(SFunction<T, ?> id, Serializable idValue, SFunction<T, ?> field, Object value, String appId) {
        return repeat(id, idValue, field, value, GlobalConst.RepeatStrategy.GLOBAL, appId);
    }

    /**
     * 字段唯一
     *
     * @param field
     * @param value
     * @return
     */
    default boolean repeatGlobal(SFunction<T, ?> field, Object value) {
        return repeat(null, null, field, value, GlobalConst.RepeatStrategy.GLOBAL);
    }

    /**
     * 机构下字段唯一
     * 更新的时候可以使用
     * <h1 style="color:red">更新使用</h1>
     *
     * @param id      主键
     * @param idValue id的值
     * @param field   判重的字段
     * @param value   判重的值
     * @return
     */
    default boolean repeatOrg(SFunction<T, ?> id, Serializable idValue, SFunction<T, ?> field, Object value) {
        return repeat(id, idValue, field, value, GlobalConst.RepeatStrategy.ORG);
    }

    /**
     * 用户下字段唯一
     * 更新的时候可以使用
     * <h1 style="color:red">更新使用</h1>
     *
     * @param id      主键
     * @param idValue id的值
     * @param field   判重的字段
     * @param value   判重的值
     * @return
     */
    default boolean repeatCreateBy(SFunction<T, ?> id, Serializable idValue, SFunction<T, ?> field, Object value) {
        return repeat(id, idValue, field, value, GlobalConst.RepeatStrategy.CREATE_BY);
    }

    /**
     * 查重
     *
     * @param id
     * @param idValue
     * @param field
     * @param value
     * @param repeatStrategy
     * @return
     */
    default boolean repeat(SFunction<T, ?> id, Serializable idValue, SFunction<T, ?> field, Object value, GlobalConst.RepeatStrategy repeatStrategy) {
        // 解析属性名
        SerializedLambda fieldResolve = LambdaUtils.resolve(field);
        String fieldName = PropertyNamer.methodToProperty(fieldResolve.getImplMethodName());
        // 驼峰转下划线
        String column = PROPERTY_COLUMN_MAP.computeIfAbsent(fieldName, key ->
                com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToUnderline(fieldName)
        );
        return repeat(idValue, column, value, repeatStrategy);
    }

    default boolean repeat(SFunction<T, ?> id, Serializable idValue, SFunction<T, ?> field, Object value, GlobalConst.RepeatStrategy repeatStrategy, String appId) {
        // 解析属性名
        SerializedLambda fieldResolve = LambdaUtils.resolve(field);
        String fieldName = PropertyNamer.methodToProperty(fieldResolve.getImplMethodName());
        // 驼峰转下划线
        String column = PROPERTY_COLUMN_MAP.computeIfAbsent(fieldName, key ->
                com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToUnderline(fieldName)
        );
        return repeat(idValue, column, value, repeatStrategy, appId);
    }

    default boolean repeat(Serializable idValue, String field, Object value, GlobalConst.RepeatStrategy repeatStrategy, String appId) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(field, value);
        if (idValue != null) {
            queryWrapper.ne(GlobalConst.ColumnField.ID, idValue);
        }
        if (GlobalConst.RepeatStrategy.ORG == repeatStrategy) {
            queryWrapper.eq(GlobalConst.ColumnField.ORG_ID, UserUtils.getCurrentUser().getOrgId());
        } else if (GlobalConst.RepeatStrategy.CREATE_BY == repeatStrategy) {
            queryWrapper.eq(GlobalConst.ColumnField.CREATE_BY, UserUtils.getCurrentUser().getId());
        }
        queryWrapper.eq("app_id", appId);
        int count = count(queryWrapper);
        return count > 0;
    }

    default boolean repeat(Serializable idValue, String field, Object value, GlobalConst.RepeatStrategy repeatStrategy) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(field, value);
        if (idValue != null) {
            queryWrapper.ne(GlobalConst.ColumnField.ID, idValue);
        }
        if (GlobalConst.RepeatStrategy.ORG == repeatStrategy) {
            queryWrapper.eq(GlobalConst.ColumnField.ORG_ID, UserUtils.getCurrentUser().getOrgId());
        } else if (GlobalConst.RepeatStrategy.CREATE_BY == repeatStrategy) {
            queryWrapper.eq(GlobalConst.ColumnField.CREATE_BY, UserUtils.getCurrentUser().getId());
        }
        int count = count(queryWrapper);
        return count > 0;
    }

    /**
     * 全局唯一
     *
     * @param idValue
     * @param field
     * @param value
     * @return
     */
    default boolean repeatGlobal(Serializable idValue, String field, Object value) {
        return repeat(idValue, field, value, GlobalConst.RepeatStrategy.GLOBAL);
    }

    /**
     * 应用下唯一
     *
     * @param idValue
     * @param field
     * @param value
     * @return
     */
    default boolean repeatGlobal(Serializable idValue, String field, Object value, String appId) {
        return repeat(idValue, field, value, GlobalConst.RepeatStrategy.GLOBAL, appId);
    }

    /**
     * 全局唯一
     *
     * @param field
     * @param value
     * @return
     */
    default boolean repeatGlobal(String field, Object value) {
        return repeat(null, field, value, GlobalConst.RepeatStrategy.GLOBAL);
    }

    /**
     * 机构下唯一进行查重
     *
     * @param idValue 主键
     * @param field
     * @param value
     * @return
     */
    default boolean repeatOrg(Serializable idValue, String field, Object value) {
        return repeat(idValue, field, value, GlobalConst.RepeatStrategy.ORG);
    }

    /**
     * 用户下唯一进行查重
     *
     * @param idValue 主键
     * @param field
     * @param value
     * @return
     */
    default boolean repeatCreteBy(Serializable idValue, String field, Object value) {
        return repeat(idValue, field, value, GlobalConst.RepeatStrategy.CREATE_BY);
    }

    ////////////////////////////////////////////////////////////////////////////////
    //   扩展的方法
    ////////////////////////////////////////////////////////////////////////////////

    /**
     * 字符串数组更改
     *
     * @param idStr
     * @return
     */
    default List<String> convert(String idStr) {
        //判断id是否为空
        if (StringUtils.isBlank(idStr)) {
            throw new GlobalException("id不允许为空");
        }
        // 将ids 拆分为数组,分隔符为"-"
        String[] idStrArr = StringUtils.split(idStr, "-");
        return Arrays.stream(idStrArr).map(id -> id).collect(Collectors.toList());
    }

    /**
     * 根据字段查询
     *
     * @param field 查询的字段；使用lambda语法
     * @param val   字段对应的值
     * @return
     */
    default T getEntityByField(SFunction<T, ?> field, Object val) {
        LambdaUpdateWrapper<T> query = new LambdaUpdateWrapper<>();
        query.eq(field, val);
        List<T> list = list(query);
        if (list == null || list.size() == 0) {
            return null;
        }
        return list.get(0);
    }

    /**
     * 根据字段获取实体集合
     *
     * @param field
     * @param val
     * @return
     */
    default List<T> getEntitiesByField(SFunction<T, ?> field, Object val) {
        LambdaUpdateWrapper<T> query = new LambdaUpdateWrapper<>();
        query.eq(field, val);
        List<T> list = list(query);
        return list;
    }

}
